package intelligentcity.model;

import intelligentcity.conf.Conf;
import intelligentcity.model.intersections.Intersection;
import intelligentcity.model.streets.HorizontalStreet;
import intelligentcity.model.streets.Street;
import intelligentcity.model.streets.VerticalStreet;
import intelligentcity.model.turns.Turn;

import java.util.ArrayList;
import java.util.List;

public class CityBuilder {

    private List<Street> streets;
    private List<Intersection> intersections;
    private List<Turn> turns;
    private List<WeightedPoint> startPoints;

    protected int leftBorderOfTraffic;
    protected int rightBorderOfTraffic;
    protected int upperBorderOfTraffic;
    protected int lowerBorderOfTraffic;

    protected int leftBorderOfStartPointRectangle;
    protected int rightBorderOfStartPointRectangle;
    protected int upperBorderOfStartPointRectangle;
    protected int lowerBorderOfStartPointRectangle;

    protected CityBuilder() {
        streets = new ArrayList<Street>();
        intersections = new ArrayList<Intersection>();
        turns = new ArrayList<Turn>();
        startPoints = new ArrayList<>();

    }

    public void addStreet(Street street) {
        streets.add(street);
    }

    public void addIntersection(VerticalStreet verticalStreet, HorizontalStreet horizontalStreet) {
        intersections.add(new Intersection(verticalStreet, horizontalStreet));
    }

    public void addIntersection(Intersection intersection) {
        intersections.add(intersection);
    }

    public void addTurn(Turn turn) {
        turns.add(turn);
    }

    public CityBuilder createStartPointsBorders() {
        leftBorderOfStartPointRectangle = leftBorderOfTraffic + Conf.distanceBetweenCityEndPointsAndStartPoints;
        rightBorderOfStartPointRectangle = rightBorderOfTraffic - Conf.distanceBetweenCityEndPointsAndStartPoints;
        upperBorderOfStartPointRectangle = upperBorderOfTraffic + Conf.distanceBetweenCityEndPointsAndStartPoints;
        lowerBorderOfStartPointRectangle = lowerBorderOfTraffic - Conf.distanceBetweenCityEndPointsAndStartPoints;
        return this;
    }

    public CityBuilder createStartPoints() {

        Point upperLeftCorner = new Point(leftBorderOfStartPointRectangle, upperBorderOfStartPointRectangle);
        Point bottomRightCorner = new Point(rightBorderOfStartPointRectangle, lowerBorderOfStartPointRectangle);

        for (Street street : streets) {
            List<WeightedPoint> potencialStartPoints = new ArrayList<>(2);
            WeightedPoint endPoint;
            if (street instanceof HorizontalStreet) {
                endPoint = new WeightedPoint(street.getFirstStreet().getP2());
                endPoint.setWeight(street.getFirstStreetImportance());
                endPoint.decX(Conf.distanceBetweenCityEndPointsAndStartPoints);
                potencialStartPoints.add(endPoint);
                endPoint = new WeightedPoint(street.getSecondStreet().getP1());
                endPoint.setWeight(street.getSecondStreetImportance());
                endPoint.incX(Conf.distanceBetweenCityEndPointsAndStartPoints);
                potencialStartPoints.add(endPoint);
            } else {
                endPoint = new WeightedPoint(street.getFirstStreet().getP1());
                endPoint.setWeight(street.getFirstStreetImportance());
                endPoint.incY(Conf.distanceBetweenCityEndPointsAndStartPoints);
                potencialStartPoints.add(endPoint);
                endPoint = new WeightedPoint(street.getSecondStreet().getP2());
                endPoint.setWeight(street.getSecondStreetImportance());
                endPoint.decY(Conf.distanceBetweenCityEndPointsAndStartPoints);
                potencialStartPoints.add(endPoint);
            }

            for (WeightedPoint p : potencialStartPoints) {
                if (!p.inside(upperLeftCorner, bottomRightCorner)) {
                    startPoints.add(p);
                }
            }
        }
        return this;
    }

    public CityBuilder createNeighbourhood() {
        List<Road> roads = new ArrayList<>();
        for (Road road : streets) {
            roads.add(road);
        }
        for (Road road : intersections) {
            roads.add(road);
        }
        for (Road road : turns) {
            roads.add(road);
        }

        for (Road road1 : roads) {

            List<Point> endPointsOfRoad1 = endPoints(road1);
            for (Road road2 : roads) {
                if (!road1.equals(road2) && !road1.getNeighbors().contains(road2)) {

                    List<Point> endPointsOfRoad2 = endPoints(road2);
                    for (Point p1 : endPointsOfRoad1) {
                        for (Point p2 : endPointsOfRoad2) {
                            if (p1.equals(p2)) {
                                road1.addNeighbour(road2);
                            }

                        }
                    }
                }

            }
        }

        return this;
    }

    private List<Point> endPoints(Road r) {

        List<Point> ep = new ArrayList<>();
        if (r instanceof Street) {
            ep.add(((Street) r).getFirstStreet().getP1());
            ep.add(((Street) r).getFirstStreet().getP2());

            ep.add(((Street) r).getSecondStreet().getP1());
            ep.add(((Street) r).getSecondStreet().getP2());
        }
        if (r instanceof Intersection) {
            ep.add(((Intersection) r).getVerticalStreet().getFirstStreet().getP1());
            ep.add(((Intersection) r).getVerticalStreet().getFirstStreet().getP2());
            ep.add(((Intersection) r).getHorizontalStreet().getFirstStreet().getP1());
            ep.add(((Intersection) r).getHorizontalStreet().getFirstStreet().getP2());

            ep.add(((Intersection) r).getVerticalStreet().getSecondStreet().getP1());
            ep.add(((Intersection) r).getVerticalStreet().getSecondStreet().getP2());
            ep.add(((Intersection) r).getHorizontalStreet().getSecondStreet().getP1());
            ep.add(((Intersection) r).getHorizontalStreet().getSecondStreet().getP2());
        }
        if (r instanceof Turn) {
            ep.add(((Turn) r).getLongerHorizontal().getP1());
            ep.add(((Turn) r).getLongerHorizontal().getP2());
            ep.add(((Turn) r).getLongerVertical().getP1());
            ep.add(((Turn) r).getLongerVertical().getP2());
            ep.add(((Turn) r).getShorterHorizontal().getP1());
            ep.add(((Turn) r).getShorterHorizontal().getP2());
            ep.add(((Turn) r).getShorterVertical().getP1());
            ep.add(((Turn) r).getShorterVertical().getP2());
        }
        return ep;
    }

    public City build() {
        return new City(streets, intersections, turns, startPoints, leftBorderOfTraffic, rightBorderOfTraffic,
                upperBorderOfTraffic, lowerBorderOfTraffic);
    }

}
